//
//  CoderOperator.swift
//  MoonKit
//
//  Created by moon on 2019/3/11.
//  Copyright © 2019 moon. All rights reserved.
//

import Foundation


infix operator .= : AssignmentPrecedence
infix operator => : AssignmentPrecedence
//infix operator <= : AssignmentPrecedence

// MARK: - .=

/// 使用编解码器操作对象
///
/// - Parameters:
///   - l: 被编解码的对象
///   - r: coder容器与编解码器组成的元组
public func .= <T,C> (l:inout T,r:(Coder,C)) where T:Codable,C:CodingConvertor,C.CodingType==T {
	switch r.0.codingOperation {
    case .decode:
        l <= r
    case .encode:
        l => r
    }
}

/// 使用编解码器操作对象
///
/// - Parameters:
///   - l: 被编解码的对象
///   - r: coder容器与编解码器组成的元组
public func .= <T,C> (l:inout T?,r:(Coder,C)) where T:Codable,C:CodingConvertor,C.CodingType==T {
    switch r.0.codingOperation {
    case .decode:
        l <= r
    case .encode:
        l => r
    }
}


/// 编码对象到coder,或者解码coder到对象中
///
/// - Parameters:
///   - l: 被编解码的对象
///   - r: coder容器
/// - Throws: `CodingError.typeMismatch`: 类型错误
/// - Throws: `CodingError.valueNotFound`: 解码出null
/// - Throws: `CodingError.keyNotFound`: key不存在
/// - Throws: `CodingError.indexOutOfRange` : codingPath中的索引超过范围
public func .= <T> (l:inout T,r:Coder) where T:Codable{
	switch r.codingOperation {
    case .decode:
        l <= r
    case .encode:
        l => r
    }
}

/// 编码对象到coder,或者解码coder到对象中
/// encoding编码时,如果对象为nil时,当coder.nilPresent为true时,才会编码nil进去
/// decoding解码时,如果codingPath对应的值不存在时,不操作.仅当codingPath解码为nil时,设置对象为nil
///
/// - Parameters:
///   - l: 被编解码的对象
///   - r: coder容器
/// - Throws: `CodingError.typeMismatch`: 类型错误
/// - Throws: `CodingError.valueNotFound`: 解码出null
/// - Throws: `CodingError.keyNotFound`: key不存在
/// - Throws: `CodingError.indexOutOfRange` : codingPath中的索引超过范围
public func .= <T> (l:inout T?,r:Coder) where T:Codable {
	switch r.codingOperation {
    case .decode:
        l <= r
    case .encode:
        l => r
    }
}


// MARK: - =>

/// 使用编解器编码coder容器
///
/// - Parameters:
///   - l: 待编码的对象
///   - r: coder容器与编解码器组成的元组
public func => <T,C> (l:T,r:(Coder,C)) where T:Codable,C:CodingConvertor,C.CodingType==T {
	let (coder,convertor) = r
	guard coder.isEncoding else { return }
	do {
		try convertor.encode(to: coder, value: l)
	} catch let error as CodingError{
       error.printError()
    } catch {
       print("error:\(error)")
    }
}

/// 使用编解器编码coder容器
///
/// - Parameters:
///   - l: 待编码的对象
///   - r: coder容器与编解码器组成的元组
public func => <T,C> (l:T?,r:(Coder,C)) where T:Codable,C:CodingConvertor,C.CodingType==T {
	let (coder,convertor) = r
	guard coder.isEncoding else { return }
	do {
		if let v = l {
			try convertor.encode(to: coder, value: v)
		} else if coder.nilPresent {
			try coder.encodeNil(coder.codingPath)
		}
	} catch let error as CodingError{
       error.printError()
    } catch {
       print("error:\(error)")
    }
}

/// 编码coder容器
///
/// - Parameters:
///   - l: 待编码对象
///   - r: coder容器
/// - Throws: `CodingError.invalidOperation`:当前操作不为编码
/// - Throws: `CodingError.typeMismatch`: 类型错误
/// - Throws: `CodingError.indexOutOfRange` : codingPath中的索引超过范围
public func => <T>(l:T,r:Coder) where T:Codable {
	guard r.isEncoding else { return }
	do {
		try r.encode(l)
	} catch let error as CodingError{
       error.printError()
    } catch {
       print("error:\(error)")
    }
}

/// 编码coder容器
/// 当对象为nil时,且coder.nilPresent为true时,才会编码nil
///
/// - Parameters:
///   - l: 待编码对象
///   - r: coder容器
/// - Throws: `CodingError.invalidOperation`:当前操作不为编码
/// - Throws: `CodingError.typeMismatch`: 类型错误
/// - Throws: `CodingError.indexOutOfRange` : codingPath中的索引超过范围
public func => <T>(l:T?,r:Coder) where T:Codable {
	guard r.isEncoding else { return }
	do {
		if let v = l {
			try r.encode(v)
		} else if r.nilPresent {
			try r.encodeNil()
		}
	} catch let error as CodingError{
       error.printError()
    } catch {
       print("error:\(error)")
    }
}




// MARK: - <=

/// 使用编解码器操作对象
///
/// - Parameters:
///   - l: 被编解码的对象
///   - r: coder容器与编解码器组成的元组
public func <= <T,C> (l:inout T,r:(Coder,C)) where T:Codable,C:CodingConvertor,C.CodingType==T {
    let (coder,convertor) = r
    do {
        try convertor.decode(from: coder,to: &l)
    } catch let error as CodingError{
        switch error {
        case .canNotDecodeTo:
            do {
                l = try convertor.decode(from: coder)
            } catch let error as CodingError{
               error.printError()
           } catch {
               print("error:\(error)")
           }
        default:
            error.printError()
        }
    } catch {
        print("error:\(error)")
    }
}
public func <= <T,C> (l:inout T?,r:(Coder,C)) where T:Codable,C:CodingConvertor,C.CodingType==T {
    let (coder,convertor) = r
    do {
        if l == nil {
            l = try convertor.decode(from: coder)
        } else {
            try convertor.decode(from: coder, to: &l)
        }
    } catch let error as CodingError{
        switch error {
        case .canNotDecodeTo:
            do {
                l = try convertor.decode(from: coder)
            } catch let error as CodingError{
               error.printError()
           } catch {
               print("error:\(error)")
           }
        default:
            error.printError()
        }
    } catch {
        print("error:\(error)")
    }
}

public func <= <T>(l:inout T?,r:Coder) where T:Codable {
    do {
        if l == nil {
            l = try r.decode()
        } else {
            try r.decode(to: &l!)
        }
    } catch let error as CodingError{
        switch error {
        case .canNotDecodeTo:
            do {
                l = try r.decode()
            } catch let error as CodingError{
               error.printError()
           } catch {
               print("error:\(error)")
           }
        default:
            error.printError()
        }
    } catch {
        print("error:\(error)")
    }
}
public func <= <T>(l:inout T,r:Coder) where T:Codable {
    do {
        try r.decode(to: &l)
    } catch let error as CodingError{
        switch error {
        case .canNotDecodeTo:
            do {
                l = try r.decode()
            } catch let error as CodingError{
               error.printError()
           } catch {
               print("error:\(error)")
           }
        default:
            error.printError()
        }
    } catch {
        print("error:\(error)")
    }
}
